package dimension.starry.dataManager;

import dimension.starry.constants.PlayerDataType;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.world.World;

import java.util.Arrays;

import static dimension.starry.constants.PlayerDataType.*;

public class PlayerDataManager {

    private static final String PLAYER_DATA_KEY = "playerData";
    private static final int PLAYER_DATA_MAX_WATER = 10000;
    private static NbtCompound clientPlayerData = null;
    private static long updateTime = 0;

    @Environment(EnvType.CLIENT)
    private static void checkNeedUpdate() {
        if (System.currentTimeMillis() - updateTime > 800 || clientPlayerData == null) {
            NetWorkRegister.sendLivingPacket();
            updateTime = System.currentTimeMillis();
        }
    }

    public static NbtCompound getSinglePlayerData(ServerWorld world, String playerName) {
        NbtCompound playerNbt = getPlayerDataFromManager(world).getCompound(playerName);
        if (playerNbt.getLong(MAX_MAGIC.getName()) == 0) {
            Arrays.stream(PlayerDataType.values()).forEach(playerData -> {
                playerData.putToNbt.accept(playerNbt, playerData.getDefaultValue.get());
            });
            setSinglePlayerDataToManager(world, playerNbt, playerName);
        }
        return playerNbt;
    }

    private static NbtCompound getPlayerDataFromManager(ServerWorld world) {
        NbtCompound NbtCompound = DataManager.getNbt(world, PLAYER_DATA_KEY);
        if (NbtCompound == null) {
            NbtCompound = new NbtCompound();
            DataManager.setNbt(world, PLAYER_DATA_KEY, NbtCompound);
        }
        return NbtCompound;
    }

    private static void setSinglePlayerDataToManager(
        ServerWorld world, NbtCompound singlePlayerData, String playerName
    ) {
        NbtCompound NbtCompound = getPlayerDataFromManager(world);
        NbtCompound.put(playerName, singlePlayerData);
        DataManager.setNbt(world, PLAYER_DATA_KEY, NbtCompound);
    }

    public static void setServerPlayerData(ServerPlayerEntity player, PlayerDataType dataType, Object value) {
        NbtCompound NbtCompound = getSinglePlayerData(player.getServerWorld(), player.getName().getString());

        if (!(player instanceof ServerPlayerEntity) || value == null) {
            return;
        } else if (WATER.equals(dataType) && (int) value > PLAYER_DATA_MAX_WATER) {
            value = PLAYER_DATA_MAX_WATER;
        } else if (WATER.equals(dataType) && (int) value < 0) {
            value = 0;
        } else if (MAGIC.equals(dataType) && (long) value > getLong(player, MAX_MAGIC)) {
            value = getLong(player, MAX_MAGIC);
        } else if (MAGIC.equals(dataType) && (long) value < 0) {
            value = 0L;
        } else if (MAX_MAGIC.equals(dataType) && (long) value < 0) {
            value = 0L;
        } else if (EXP.equals(dataType) && (long) value < 0) {
            value = 0L;
        } else if (LEVEL.equals(dataType) && (int) value < 0) {
            value = 0;
        }

        dataType.putToNbt.accept(NbtCompound, value);
        setSinglePlayerDataToManager(player.getServerWorld(), NbtCompound, player.getName().getString());
    }

    public static long getLong(PlayerEntity player, PlayerDataType dataType) {
        if (player instanceof ServerPlayerEntity) {
            return Long.parseLong(String.valueOf(dataType.getFromNbt.apply(
                getSinglePlayerData(((ServerPlayerEntity) player).getServerWorld(), player.getName().getString()))));
        } else {
            checkNeedUpdate();
            if (clientPlayerData != null) {
                return clientPlayerData.getLong(dataType.getName());
            } else {
                return Long.parseLong(String.valueOf(dataType.getDefaultValue.get()));
            }
        }
    }

    public static int getInt(PlayerEntity player, PlayerDataType dataType) {
        if (player instanceof ServerPlayerEntity) {
            return Integer.parseInt(String.valueOf(dataType.getFromNbt.apply(
                getSinglePlayerData(((ServerPlayerEntity) player).getServerWorld(), player.getName().getString()))));
        } else {
            checkNeedUpdate();
            if (clientPlayerData != null) {
                return clientPlayerData.getInt(dataType.getName());
            } else {
                return Integer.parseInt(String.valueOf(dataType.getDefaultValue.get()));
            }
        }
    }

    public static double getDouble(PlayerEntity player, PlayerDataType dataType) {
        if (player instanceof ServerPlayerEntity) {
            return (double) dataType.getFromNbt.apply(
                getSinglePlayerData(((ServerPlayerEntity) player).getServerWorld(), player.getName().getString()));
        } else {
            checkNeedUpdate();
            if (clientPlayerData != null) {
                return clientPlayerData.getDouble(dataType.getName());
            } else {
                return (double) dataType.getDefaultValue.get();
            }
        }
    }

    public static ItemStack getItemStack(PlayerEntity player, PlayerDataType dataType) {
        if (player instanceof ServerPlayerEntity) {
            return ItemStack.fromNbt((NbtCompound) dataType.getFromNbt.apply(
                getSinglePlayerData(((ServerPlayerEntity) player).getServerWorld(), player.getName().getString())));
        } else {
            checkNeedUpdate();
            if (clientPlayerData != null && clientPlayerData.contains(dataType.getName())) {
                return ItemStack.fromNbt((NbtCompound) clientPlayerData.get(dataType.getName()));
            } else {
                return (ItemStack) dataType.getDefaultValue.get();
            }
        }
    }

    public static void setClientPlayerData(NbtCompound playerData) {
        clientPlayerData = playerData;
    }

}
